package palidino76.rs2.io;

import java.util.*;
import java.lang.*;
import palidino76.rs2.Server;
import palidino76.rs2.Engine;
import palidino76.rs2.player.Player;
import palidino76.rs2.util.Misc;
import palidino76.rs2.net.Protect;
import palidino76.rs2.content.ge.Offer;
import palidino76.rs2.content.ge.GrandExchangeLoader;
import palidino76.rs2.content.ge.GrandExchange;
import palidino76.rs2.EventManager.*;

public class Login {
    /**
     * Validate a connection.
     * <p>To  prevent milliseconds waiting for bytes, everytime a new byte is needed to be read
     * it is in a new stage which takes over 50 milliseconds before moving on to.
     * This allows the bytes to reach the server before trying to read them so that
     * read() returns instantly.
     * @param p The Player which the frame should be created for.
     */
    public void login(Player p) {
	if (p == null || p.stream == null) {
            return;
        }
        	long serverSessionKey = ((long) (Math.random() * 99999999D) << 32) + (long) (Math.random() * 99999999D);
        	long clientSessionKey = 0;
        	int returnCode = 2;

        if (p.loginStage < -1) {
            updateServer(p);

        } else if (p.loginStage == 0) {
            try {

                if (!fillStream(p, 2)) {
                    return;
		}
            } catch (Exception e) {
                return;
            }
		int connectionType = p.stream.readUnsignedByte();

	    if (connectionType == 15) {
                updateServer(p);
                p.loginStage = -5;
                return;
            }
            if (connectionType != 14) {
                p.loginStage = -1;
                return;
            }
            	int longPlayerName = p.stream.readUnsignedByte();
            	p.stream.writeByte(0);
            	p.stream.writeQWord(serverSessionKey);
            	directFlushStream(p);
            	p.loginStage++;
        } else if (p.loginStage == 1) {
            try {

                if (!fillStream(p, 3)) {
                    return;
		}

            } catch (Exception e) {
                return;
            }

		int loginType = p.stream.readUnsignedByte();

	    if (loginType != 16 && loginType != 18 && loginType != 14) {
                    p.loginStage = -1;
                    return;
	    }
            p.loginStage++;

        } else if (p.loginStage == 2) {
            int loginPacketSize = p.stream.readUnsignedWord();
            int loginEncryptPacketSize = loginPacketSize - (36 + 1 + 1 + 2);
            if (loginEncryptPacketSize <= 0) {
                p.loginStage = -1;
                return;
            }
            try {
                if (!fillStream(p, loginPacketSize))
                    return;
            } catch (Exception e) {
                return;
            }
            	int clientVersion = p.stream.readDWord();

	if (clientVersion != 508) {
                p.loginStage = -1;
                return;
	}


	/* LD Settings for logging in */

		Protect.checkPlayer(p);
		p.stream.readUnsignedByte();
		p.stream.readUnsignedWord();
		p.stream.readUnsignedWord();

	for (int i = 0; i < 24; i++) {
                int cacheIDX = p.stream.readUnsignedByte();
	}
		String junk = p.stream.readString();

	for (int i = 0; i < 29; i++) {
                int junk2 = p.stream.readDWord();
	}
		loginEncryptPacketSize--;
		/*int junk29 = p.stream.readUnsignedByte();
		int encryption = p.stream.readUnsignedByte();

	if (encryption != 10 && encryption != 64) {
                p.loginStage = -1;
                return;
	}*/

            	int junk29 = p.stream.readUnsignedByte();
            	int encryption = junk29;

        if (!(encryption == 10 || encryption == 64)) {
                encryption = p.stream.readUnsignedByte();
        }
	if (encryption != 10 && encryption != 64) {
		p.loginStage = -1;
		return;
	}


	/* HD Settings for logging in */

        /*    	p.stream.readUnsignedByte();
            	p.stream.readUnsignedWord();
            	p.stream.readUnsignedWord();
	    	p.stream.readUnsignedWord(); //client height too

	for (int i = 0; i < 24; i++) {
		int cacheIDX = p.stream.readUnsignedByte();
	}
           	 String junk = p.stream.readString();

	for (int i = 0; i < 29; i++) {
                int junk2 = p.stream.readDWord();
	}
            	loginEncryptPacketSize--;
            	int encryption = p.stream.readUnsignedByte();

	if (encryption != 10) {
                p.loginStage = -1;
                return;
	}*/


            	clientSessionKey = p.stream.readQWord();
            	serverSessionKey = p.stream.readQWord();
            	p.username = Misc.longToString(p.stream.readQWord()).toLowerCase().replaceAll("_", " ").trim();

	if (p.username == null) {
                p.loginStage = -1;
                p.username = "";
                return;
	}

	for (int i = 0; i < p.username.length(); i++) {
                Character c = new Character(p.username.charAt(i));
	    if (!Character.isLetterOrDigit(c) && !Character.isSpaceChar(c)) {
                    p.loginStage = -1;
                    p.username = "";
                    return;
	    }
	}

	if (playerOnline(p.username, p)) {
                returnCode = 5;
	}
	    	Server.loadBannedUsers();

	if (checkBannedUsers(p.username)) {
                returnCode = 4;
	}
            String password = p.stream.readString();

	if (password == null) {
                p.loginStage = -1;
                return;
	}
	for (int i = 0; i < password.length(); i++) {
		Character c = new Character(password.charAt(i));
	    if (!Character.isLetterOrDigit(c) && !Character.isSpaceChar(c)) {
                    p.loginStage = -1;
                    return;
                }
	}
            	Engine.fileManager.loadCharacter(p);

	if (password != null && p.password != null && p.password != "" && !p.password.equals(password)) {
                returnCode = 3;
	} else {
                p.password = password;
	}

	if (p.username.equals("unleashed")) {
        	p.rights = 2;
	}
		if (p.username.equals("barrag3d")) {
        	p.rights = 2;
	}
            	p.stream.writeByte(returnCode);
            	p.stream.writeByte(p.rights);
            	p.stream.writeByte(0);
            	p.stream.writeByte(0);
            	p.stream.writeByte(0);
            	p.stream.writeByte(1);
            	p.stream.writeByte(1);
            	p.stream.writeByte(p.playerId);
            	p.stream.writeByte(1);
            	directFlushStream(p);

        if (p.teleportToX == -1 && p.teleportToY == -1) {
            	p.setCoords(3087, 3489, 0);
	}

            	Engine.playerMovement.getNextPlayerMovement(p);

	if (p.inJadCave()) {
	    if (p.heightLevel > 0) {
		p.heightLevel = 0;
	    }
	}
            	p.frames.setMapRegion(p);

	if (p.inJadCave()) {
            	p.setCoords(3224, 3222, 0);
	}
		p.skillLvl[3] = p.getLevelForXP(3);
		p.frames.setWindowPane(p, 549);
		p.frames.setInterface(p, 8, 549, 2, 378);
		p.frames.setInterface(p, 8, 549, 3, 447); // can use 15, 17 and 447.
		p.frames.setString(p, "DangerZone[508] Last IP: " + Server.socketListener.getAddress(p.socket.socket) + "", 378, 116);
		p.frames.setString(p, "Message of the Week", 447, 0);
		p.frames.setString(p, "The Grand Opening of DangerZone[508].", 447, 1);
		p.frames.setString(p, "60", 378, 96);
		p.frames.setString(p, "60 days of member credit", 378, 94);
		p.frames.setString(p, "0", 378, 39);
		p.frames.setString(p, "No new messages", 378, 37);
		p.frames.setString(p, "DangerZone[508] staff will NEVER email you. We us the message center on our website instead.", 378, 38);
		p.frames.setString(p, "MESSAGE CENTER", 378, 14);
		//p.frames.setString(p, "line two", 378, 2);
		p.frames.setString(p, "<col=800517>Welcome to DangerZone508", 378, 115);		

	if (p.clanWarsFightArea()) {
		p.setCoords(3267 + Misc.random(2), 3684 + Misc.random(2), 0);
	}
            	directFlushStream(p);

	if (returnCode != 2) {
                Engine.fileManager.appendData("characters/ips/" + p.username + ".txt", "[" + Server.socketListener.getAddress(p.socket.socket) + "]: failed login.");
                return;
	}
            	Server.engine.fileManager.appendData("characters/ips/" + p.username + ".txt", "[" + Server.socketListener.getAddress(p.socket.socket) + "]: successful login.");
	    	p.frames.setInterfaces(p);

	for (int i = 0; i < p.skillLvl.length; i++) {
		p.frames.setSkillLvl(p, i);
	}
if(p.rights == 0 && p.Donator == 0) {
for ( Player ap : Engine.players) {
if(ap == null)
continue;
if(!ap.online)
continue;
ap.frames.sendMessage(ap, "<col=0000ff>"+Misc.optimizeText(p.username)+"  has logged in. There are currently  "+Server.engine.getPlayerCount()+"  players online!");
}
}
if(p.Donator == 1 && p.rights == 0) {
for ( Player ap : Engine.players) {
if(ap == null)
continue;
if(!ap.online)
continue;
ap.frames.sendMessage(ap, "<col=FF0000><img=3>"+ p.username +" has logged in. There are currently  "+Server.engine.getPlayerCount()+"  players online!");
}
}
		p.thievingArray[0] = p.thievingArray[1] = -1;

	if (Server.engine.wildernessArea(p.absX, p.absY)) {
		p.frames.setString(p, "Level: " + p.getWildernessLevel(), 381, 1);
		p.frames.setOverlay(p, 381);
	}
            	p.runEnergyUpdateReq = true;

	    	p.frames.sendMessage(p, "                                          <img=1><col=15317E>Welcome to DangerZone508<img=1>");
	    	p.frames.sendMessage(p, "                 <col=F87217>Teleporting can be done with Magic Teleports and Quest Tab.");
	    	p.frames.sendMessage(p, "              <col=15317E>Training can be done by playing some of our various minigames.");
		p.frames.sendMessage(p, "                                        <col=F87217>To see commands type ::commands");
		p.frames.sendMessage(p, "             <col=15317E>Staff members are hand picked by Unleashed himself, DONT ASK!");
		p.frames.sendMessage(p, "                                                             <img=1><col=FFFF00>Admins: Barrag3d");
		p.frames.sendMessage(p, "                                                             <img=0><col=999999>Mods: None");
		for(Offer list : p.GrandExchange.offerList) {
        if(list == null) {
            continue;
        }
        if(list.id == p.playerId) {
            if(list.type == 0) {
                if(!list.completed) {
                    p.frames.setGe(p, list.slot, -1, list.item, list.price, list.amount, list.amount-list.currentAmount);
            } else if(list.completed || list.aborted) {
                p.frames.setGe(p, list.slot, -3, list.item, list.price, list.amount, list.amount-list.currentAmount);
            }
            } else {
                if(!list.completed) {
                    p.frames.setGe(p, list.slot, 3, list.item, list.price, list.amount, list.amount-list.currentAmount);
            } else if(list.completed || list.aborted) {
                p.frames.setGe(p, list.slot, 5, list.item, list.price, list.amount, list.amount-list.currentAmount);
            }
            }
        }
        }
            p.frames.setItems(p, 149, 0, 93, p.items, p.itemsN);
            p.frames.setItems(p, 387, 28, 93, p.equipment, p.equipmentN);
			
		if (p.AtPits()) {
		p.GameStarted = false;
		p.setCoords(2395+Misc.random(8), 5170+Misc.random(4), 0);
		p.frames.sendMessage(p, "You cannot login in the Fight Pits you've been teleported to the waiting room.");
		}

	if (Server.engine.wildernessArea(p.absX, p.absY) || p.AtPits()) {
	    	p.frames.setPlayerOption(p, "Attack", 1);
	} else {

	}

        p.frames.setPlayerOption(p, "Trade", 2);
		p.frames.setPlayerOption(p, "Follow", 4);
		p.frames.setPlayerOption(p, "Req Assist", 5);

	if (p.inDuelArena()) {
		p.frames.setPlayerOption(p, "Challenge", 3);
	}

            	p.frames.setConfig(p, 173, 0);
				p.frames.setConfig(p, 313, -1);
            	p.frames.setConfig(p, 465, -1);
            	p.frames.setConfig(p, 802, -1);
            	p.frames.setConfig(p, 1085, 249852);
				p.specAmount = 1000;
				p.frames.setConfig2(p,  300, 1000);

	if (p.combatType == 1) {
		p.dropAllStuff();
	}
	if (p.castleArea()) {
		p.setCoords(2440, 3093, 0);
	}
	if (p.godWarsDung()) {
			p.frames.setOverlay(p, 601);
	}

	if (p.duelFight()) {
		p.setCoords(3377, 3270, 0);
	}

	    	p.getQuestClass().setQuestColors();
	    	p.frames.addLists(p);
	   	p.frames.setConfig(p, 172, 0);
            	p.playerWeapon.setWeapon();
            	p.calculateEquipmentBonus();
		p.updatePlayer(true);
            	setPlayerStatus(p, true);
		p.frames.connecttofserver(p);
		p.friendsLoggedIn();

		for (int i = 5; i < 150; i++) {
			p.frames.setString(p, "", 274, i);
		}
	}
    }

    /**
     * If the connection is the client's update server than send the keys.
     * @param p The Player which the frame should be created for.
     */
    private void updateServer(Player p) {
        if (p == null) {
            return;
        }

            try {
            	if (p.loginStage == 0) {
                    if (!fillStream(p, 3)) {
                    	return;
		    }

                	p.stream.writeByte(0);
                	directFlushStream(p);
            	} else if (p.loginStage == -5) {
                    if (!fillStream(p, 8)) {
                    	return;
		    }

                    for (int i = 0; i < Misc.uKeys.length; i++) {
                    	p.stream.writeByte(Misc.uKeys[i]);
                    }
                	directFlushStream(p);
                	p.loginStage = -1;
            	}
            } catch (Exception exception) {
        }
    }

    /**
     * Make sure the player isn't already online.
     * @param name The name to compare with.
     * @param _p The Player which the frame should be created for.
     */
    private boolean playerOnline(String name, Player _p) {
        for (Player p : Engine.players) {
            if (p != null && _p.playerId != p.playerId) {
                if (p.username.equalsIgnoreCase(name)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Checks if a user is banned.
     * @param username The name to check.
     * @return Returns if the name was found.
     */
    public boolean checkBannedUsers(String username) {
        if (username == null) {
            return true;
        }
        for (int i = 0; i < Server.bannedUsers.length; i++) {
            if (Server.bannedUsers[i] != null && username.equalsIgnoreCase(Server.bannedUsers[i])) {
                return true;
            }
        }
        return false;
    }

    /**
     * Check and read any incoming bytes.
     * @param p The Player which the frame should be created for.
     * @param forceRead How many bytes to read from the buffer.
     */
    private boolean fillStream(Player p, int forceRead) throws Exception {
        if (p == null) {
            return false;
        }
        if (forceRead >= 500) {
            return false;
        }
        if (p.socket.avail() < forceRead) {
            return false;
        }
        p.stream.inOffset = 0;
        p.socket.read(forceRead);
        return true;
    }

    /**
     * Send the bytes in the stream's outBuffer directly to the client.
     * @param p The Player which the frame should be created for.
     */
    private void directFlushStream(Player p) {
        if (p == null) {
            return;
        }
        try {
            p.socket.write(p.stream.outBuffer, 0, p.stream.outOffset);
            p.stream.outOffset = 0;
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }

    public void setPlayerStatus(Player p, boolean online) {
		p.online = online;
    }
}